import NotificationCenter from "../notificationCenter/index.js";

import {
	getUUID
} from "../utils/index.js";

import {
	logInfo,
	logError
} from "../logCenter/index.js";

import RequestUtils, {
	handlerParam,
	packRequest2Promise
} from "./requestUtils.js";

import RequestModels, {
	RequestParam,
	UploadParam,
	DownloadParam
} from './requestModels.js';

import {
	ResponseDownloadResult,
	ResponseFailResult,
	ResponseResult
} from "./responseModels.js";

let _requestConfig;
/**
 * 注册请求工具模块
 * @param {*} requestConfig 
 */
function registerModule(requestConfig) {
	_requestConfig = requestConfig;
	RequestModels.registerModels(requestConfig);
	RequestUtils.registerUtils(requestConfig);
}

/**
 * GET
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function getRequest(param, callback) {
	if (!(param instanceof RequestParam)) throw new Error(
		'getRequest param must be a RequestParam object');
	return packRequest2Promise(_httpRequest, {
		...param,
		method: 'GET'
	}, callback);
}

/**
 * POST
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function postRequest(param, callback) {
	if (!(param instanceof RequestParam)) throw new Error(
		'postRequest param must be a RequestParam object');
	return packRequest2Promise(_httpRequest, {
		...param,
		method: 'POST'
	}, callback);
}

/**
 * PUT
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function putRequest(param, callback) {
	if (!(param instanceof RequestParam)) throw new Error(
		'putRequest param must be a RequestParam object');
	return packRequest2Promise(_httpRequest, {
		...param,
		method: 'PUT'
	}, callback);
}

/**
 * DELETE
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function deleteRequest(param, callback) {
	if (!(param instanceof RequestParam)) throw new Error(
		'deleteRequest param must be a RequestParam object');
	return packRequest2Promise(_httpRequest, {
		...param,
		method: 'DELETE'
	}, callback);
}

/**
 * UPLOAD
 * @param {UploadParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function uploadRequest(param, callback) {
	if (!(param instanceof UploadParam)) throw new Error(
		'uploadRequest param must be a UploadParam object');
	return packRequest2Promise(_httpUpload, param, callback);
}

/**
 * DOWNLOAD
 * @param {DownloadParam} param
 * @param {Function} callback 请求结果回调函数
 * @returns {Promise} 返回一个Promise对象
 */
function downloadRequest(param, callback) {
	if (!(param instanceof DownloadParam)) throw new Error(
		'downloadRequest param must be a DownloadParam object');
	return packRequest2Promise(_httpDownload, param, callback);
}

/**
 * 数据请求
 * @param {RequestParam} requestParam
 * @param {Function} callback 请求结果回调函数
 */
function _httpRequest(requestParam, callback) {
	// 处理请求参数
	let param = handlerParam(requestParam);
	logInfo("start request", requestParam);
	// 调用uni.requestTask方法，上传文件
	const requestTask = uni.request({
		...param,
		// 上传成功时
		success: (res) => {
			_handlerSuccess(res, requestParam, callback, "request");
		},
		// 上传失败时
		fail: (err) => {
			_handlerFailResponse(err, requestParam, callback, "request");
		}
	})
	// 如果requestParam对象中有signal属性
	// 将uploadParam.signal.abort方法赋值给requestTask.abort方法
	requestParam.signal && (requestParam.signal.abort = () => {
		requestTask.abort()
	})
}

/**
 * 上传请求
 * @param {UploadParam} uploadParam
 * @param {Function} callback 请求结果回调函数
 */
function _httpUpload(uploadParam, callback) {
	// 处理请求参数
	let param = handlerParam(uploadParam);
	// 调用uni.uploadFile方法，上传文件
	const requestTask = uni.uploadFile({
		// 将上传参数传递给uni.uploadFile方法
		...param,
		// 上传成功时
		success: (res) => {
			_handlerSuccess(res, uploadParam, callback, "upload");
		},
		// 上传失败时
		fail: (err) => {
			_handlerFailResponse(err, uploadParam, callback, "upload");
		}
	})
	// 如果uploadParam.signal存在，
	// signal.abort
	// signal.onProgressUpdate
	// signal.offProgressUpdate
	if (uploadParam.signal) {
		uploadParam.signal.abort = () => {
			requestTask.abort()
		}
		if (typeof uploadParam.signal.onProgressUpdate == 'function') {
			requestTask.onProgressUpdate((res) => {
				uploadParam.signal.onProgressUpdate(res);
			})
		}
		uploadParam.signal.offProgressUpdate = (callback) => {
			requestTask.offProgressUpdate(callback)
		}
	}
}

/**
 * 下载请求
 * @param {DownloadParam} downloadParam 
 * @param {Function} callback 请求结果回调函数
 */
function _httpDownload(downloadParam, callback) {
	// 处理请求参数
	let param = handlerParam(downloadParam);
	// 调用uni.downloadFile方法，开始下载文件
	const requestTask = uni.downloadFile({
		// 将下载参数传入
		...param,
		// 下载成功时
		success: (res) => {
			_handlerSuccess(res, downloadParam, callback, "download");
		},
		// 下载失败时
		fail: (err) => {
			_handlerFailResponse(err, downloadParam, callback, "download");
		}
	})
	// 如果下载参数中有signal属性
	// signal.abort
	// signal.onProgressUpdate
	// signal.offProgressUpdate
	if (downloadParam.signal) {
		// 中断下载任务
		downloadParam.signal.abort = () => {
			requestTask.abort();
		}
		// 如果下载参数中有onProgressUpdate方法，则设置onProgressUpdate方法，用于更新下载进度
		if (typeof downloadParam.signal.onProgressUpdate == 'function') {
			requestTask.onProgressUpdate((res) => {
				downloadParam.signal.onProgressUpdate(res);
			})
		}
		// 取消下载进度更新监听
		downloadParam.signal.offProgressUpdate = (callback) => {
			requestTask.offProgressUpdate(callback)
		}
	}
}

// 定义一个处理失败响应的函数
/**
 * @param {Error} err
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @param {String} type 请求类型
 */
function _handlerFailResponse(err, param, callback, type = 'request') {
	logError("request fail", err, param, type);
	// 创建一个ResponseFailResult对象
	let result = new ResponseFailResult(err);
	// 如果result对象中有errMsg属性，则使用errMsg属性，否则使用默认的fail：errCode
	let errMsg = result.errMsg ? result.errMsg : `fail：${result.errCode}`;
	// 如果callback参数是一个函数，则调用callback函数，并传入false和errMsg作为参数
	typeof callback == 'function' && callback(false, errMsg);
}

// 处理请求成功的函数
/**
 * @param {Response} res
 * @param {RequestParam} param
 * @param {Function} callback 请求结果回调函数
 * @param {String} type 请求类型
 */
function _handlerSuccess(res, param, callback, type = 'request') {
	logInfo("request success", res, param, type);
	// 创建一个ResponseResult对象
	let result = new ResponseResult(res);
	// 如果状态码为200 链接成功
	if (result.statusCode === _requestConfig.successStatusCode) {
		if (type == 'download') {
			// download 返回值 {statusCode, tempFilePath, filePath}
			result = new ResponseDownloadResult(res);
			typeof callback == 'function' && callback(true, result);
		} else {
			// request 返回值 {statusCode, data, header}
			// upload 返回值 {statusCode, data}
			// 如果返回的数据code为200，业务处理成功
			if (result.data.code === _requestConfig.successCode) { // 请求成功
				// 如果callback是一个函数，则调用callback函数，并传入true和返回的数据
				typeof callback == 'function' && callback(true, result.data.data);
			} else if (result.data.code === _requestConfig.reloginCode) {
				// 如果返回的数据code为401 token过期, 将请求数据存储起来，等待更新token后重新请求
				_waitRefreshTokenRequery(param, callback, type);
				typeof callback == 'function' && callback(false, '');
			} else {
				// 如果返回的数据code不为200和401,请求失败
				// 获取错误信息
				let errMsg = result.data.msg ? result.data.msg : `error：${result.data.code}`;
				// 如果callback是一个函数，则调用callback函数，并传入false和错误信息
				typeof callback == 'function' && callback(false, errMsg);
			}
		}

	} else {
		// 如果状态码不为200 链接失败
		// 获取错误信息
		let errMsg = `statusError：${result.statusCode}`
		typeof callback == 'function' && callback(false, errMsg);
	}
}

// 因token过期返回401，等待重新请求的队列
let WAIT_401_REQUERY_LIST = [];
const _wait401RequeryObserverSymbol = `wait401RequeryObserverSymbol-${getUUID()}`;

/**
 * 等待token刷新后重新请求
 * @param {Object} param 原请求参数
 * @param {Object} callback 原回调函数
 * @param {Object} type	请求类型 request、upload、download
 */
function _waitRefreshTokenRequery(param, callback, type) {
	// 将param、callback和type添加到WAIT_401_REQUERY_LIST数组中
	WAIT_401_REQUERY_LIST.push({
		param,
		callback,
		type
	});
	// 重置观察者，当NOTIFICATION_REFRESH_TOKEN_SUCCESS通知被触发时，执行回调函数
	NotificationCenter.addNormalNotificationObserver(_requestConfig.refreshTokenNotificationSuccessKey, (info) => {
		// 遍历WAIT_401_REQUERY_LIST数组
		WAIT_401_REQUERY_LIST.forEach((item) => {
			// 根据type的值，执行不同的请求函数
			switch (item.type) {
				case 'request':
					_httpRequest(item.param, item.callback);
					break;
				case 'upload':
					_httpUpload(item.param, item.callback);
					break;
				case 'download':
					_httpDownload(item.param, item.callback);
					break;
				default:
					logError("request fail", "请求类型错误", item);
					break;
			}
		})
		// 清空WAIT_401_REQUERY_LIST数组
		WAIT_401_REQUERY_LIST = [];
		// 完成401处理，移除观察者，直到下次401再重新创建观察者
		NotificationCenter.removeNotificationObserver(_requestConfig.refreshTokenNotificationSuccessKey,
			_wait401RequeryObserverSymbol);
	}, _wait401RequeryObserverSymbol);
	// 发送NOTIFICATION_REFRESH_TOKEN通知， 通知刷新token
	NotificationCenter.postNotification(_requestConfig.refreshTokenNotificationKey);
}

module.exports = {
	registerModule,

	getRequest,
	postRequest,
	putRequest,
	deleteRequest,
	uploadRequest,
	downloadRequest,

	RequestParam,
	UploadParam,
	DownloadParam
}